package org.eclipse.buildship.core.workspace.internal

import com.google.common.base.Optional

import com.gradleware.tooling.toolingmodel.OmniEclipseBuildCommand

import org.eclipse.core.resources.ICommand
import org.eclipse.core.resources.IProject
import org.eclipse.core.runtime.NullProgressMonitor

import org.eclipse.buildship.core.configuration.GradleProjectBuilder
import org.eclipse.buildship.core.test.fixtures.WorkspaceSpecification

class BuildCommandUpdaterTest extends WorkspaceSpecification {

    def "Build command can be set on a project"() {
        given:
        def project = newProject('sample-project')

        when:
        BuildCommandUpdater.update(project, oneBuildCommand('customBuildCommand', ['key' : 'value']), new NullProgressMonitor())

        then:
        hasBuildCommand(project, 'customBuildCommand', ['key' : 'value'])
    }

    def "Gradle builder is added when nature information is absent"() {
        given:
        def project = newProject('sample-project')

        when:
        BuildCommandUpdater.update(project, unsupportedBuildCommands(), new NullProgressMonitor())

        then:
        hasBuildCommand(project, GradleProjectBuilder.ID)
    }

    def "Gradle builder is added when nature information is present"() {
        given:
        def project = newProject('sample-project')

        when:
        BuildCommandUpdater.update(project, zeroBuildCommands(), new NullProgressMonitor())

        then:
        hasBuildCommand(project, GradleProjectBuilder.ID)
    }

    def "Gradle builder is added only once"() {
        given:
        def project = newProject('sample-project')

        when:
        BuildCommandUpdater.update(project, oneBuildCommand(GradleProjectBuilder.ID), new NullProgressMonitor())

        then:
        project.description.buildSpec.length == 1
        hasBuildCommand(project, GradleProjectBuilder.ID)
    }

    def "Can have two build commands with same id and different arguments"() {
        given:
        def project = newProject('sample-project')

        when:
        def buildCommands = twoBuildCommands('customBuildCommand', ['key' : 'value'], 'customBuildCommand', ['key' : 'otherValue'])
        BuildCommandUpdater.update(project, buildCommands, new NullProgressMonitor())

        then:
        hasBuildCommand(project, 'customBuildCommand', ['key' : 'value'])
        hasBuildCommand(project, 'customBuildCommand', ['key' : 'otherValue'])
    }

    def "Build commands are removed if they no longer exist in the Gradle model"() {
        given:
        def project = newProject('sample-project')

        when:
        BuildCommandUpdater.update(project, oneBuildCommand('customBuildCommand', ['key' : 'value']), new NullProgressMonitor())
        BuildCommandUpdater.update(project, zeroBuildCommands(), new NullProgressMonitor())

        then:
        !hasBuildCommand(project,, 'customBuildCommand', ['key' : 'value'])
    }

    def "Manually added build commands are preserved if Gradle model does not provide them"() {
        given:
        def project = newProject('sample-project')
        def description = project.description
        def command = description.newCommand()
        command.setBuilderName('manuallyCreatedBuildCommand')
        command.setArguments([:])
        def commands = description.buildSpec + command
        description.setBuildSpec(commands as ICommand[])
        project.setDescription(description, new NullProgressMonitor())

        when:
        BuildCommandUpdater.update(project, unsupportedBuildCommands(), new NullProgressMonitor())

        then:
        hasBuildCommand(project, 'manuallyCreatedBuildCommand')
    }

    def "Manually added build commands are removed if Gradle model provides them"() {
        given:
        def project = newProject('sample-project')
        def description = project.description
        def command = description.newCommand()
        command.setBuilderName('manuallyCreatedBuildCommand')
        command.setArguments([:])
        def commands = description.buildSpec + command
        description.setBuildSpec(commands as ICommand[])
        project.setDescription(description, new NullProgressMonitor())

        when:
        BuildCommandUpdater.update(project, zeroBuildCommands(), new NullProgressMonitor())

        then:
        !hasBuildCommand(project, 'manuallyCreatedBuildCommand')
    }

    private Optional unsupportedBuildCommands() {
        Optional.absent()
    }

    private Optional zeroBuildCommands() {
        Optional.of([])
    }

    private Optional oneBuildCommand(name, arguments = [:]) {
        Optional.of([buildCommand(name, arguments)])
    }

    private Optional twoBuildCommands(name1, arguments1, name2, arguments2) {
        Optional.of([buildCommand(name1, arguments1), buildCommand(name2, arguments2)])
    }

    private OmniEclipseBuildCommand buildCommand(name, arguments = [:]) {
        OmniEclipseBuildCommand mockedBuildCommand = Mock(OmniEclipseBuildCommand)
        mockedBuildCommand.name >> name
        mockedBuildCommand.arguments >> arguments
        mockedBuildCommand
    }

    private Boolean hasBuildCommand(IProject project, String builderName, Map arguments = [:]) {
        project.description.buildSpec.find { it.builderName == builderName && it.arguments == arguments } != null
    }

}
